﻿using System;
using System.Collections.Generic;
using System.Linq;
using DG.Tweening;
using Spine;
using Spine.Unity;
using Spine.Unity.AttachmentTools;
using UnityEngine;
using Object = UnityEngine.Object;

namespace Core.Extension
{
    /// <summary>
    /// 对Spine插件的拓展
    /// </summary>
    public static class SpineExtension
    {
        private static Dictionary<string, Skin> _skinDic = new Dictionary<string, Skin>();

        /// 所有已经关闭的槽
        private static Dictionary<SkeletonAnimation, List<string>> _allClosedSlot = new();

        public static TrackEntry SetAnimation(this SkeletonGraphic skeletonGraphic, string animationName,
            bool isLoop = false, float timeState = 0F)
        {
            Log($"SetAnimation_{animationName} 占用通道为： 0");
            skeletonGraphic.timeScale = 1;
            var spine = skeletonGraphic.AnimationState.SetAnimation(0, animationName, isLoop);
            spine.AnimationStart = timeState;
            return spine;
        }

        public static TrackEntry SetAnimation(this SkeletonAnimation skeletonAnimation, string animationName,
            bool isLoop = false, float timeState = 0F)
        {
            Log($"SetAnimation_{animationName} 占用通道为： 0");
            skeletonAnimation.Resume();
            var spine = skeletonAnimation.AnimationState.SetAnimation(0, animationName, isLoop);
            spine.AnimationStart = timeState;
            return spine;
        }

        /// <summary>
        /// 重载设置动画
        /// </summary>
        public static TrackEntry SetAnimation(this SkeletonAnimation skeletonAnimation, string animationName,
            int trackIndex,
            bool isLoop = false, float timeState = 0F)
        {
            Log($"SetAnimation_{animationName} 占用通道为： {trackIndex}");
            skeletonAnimation.Resume();
            var spine = skeletonAnimation.AnimationState.SetAnimation(trackIndex, animationName, isLoop);
            spine.AnimationStart = timeState;
            return spine;
        }

        public static void SetAnimation(this SkeletonAnimation skeletonAnimation, List<string> allAnim,
            Action finishCb)
        {
            skeletonAnimation.SetAnimation(allAnim[0]).Complete += delegate(TrackEntry entry)
            {
                allAnim.RemoveAt(0);
                if (allAnim.Count == 0)
                    finishCb?.Invoke();
                else
                    skeletonAnimation.SetAnimation(allAnim, finishCb);
            };
        }

        public static float Pause(this SkeletonAnimation skeletonAnimation)
        {
            return skeletonAnimation.timeScale = 0;
        }

        public static float Resume(this SkeletonAnimation skeletonAnimation)
        {
            return skeletonAnimation.timeScale = 1;
        }

        /// <summary>
        /// 更改Spine的速度
        /// </summary>
        public static float ChangeSpineSpeed(this SkeletonAnimation skeletonAnimation, float timeScale)
        {
            return skeletonAnimation.timeScale = timeScale;
        }

        /// 打开槽
        public static void OpenSlot(this SkeletonGraphic spine, string slotName)
        {
            if (spine.Skeleton.FindSlot(slotName) == null) return;
            spine.Skeleton.SetAttachment(slotName, slotName);
            Log($"slotName: {slotName} 已经打开");
        }

        /// 关闭槽
        public static void CloseSlot(this SkeletonGraphic spine, string slotName)
        {
            if (spine.Skeleton.FindSlot(slotName) == null)
            {
                Debug.Log(string.Format("<color=#ff0000>{0}</color>", $"{slotName} ： 未找到"));
                return;
            }

            spine.Skeleton.SetAttachment(slotName, null);
            Log($"slotName: {slotName} 已经关闭");
        }

        /// 打开槽
        public static void OpenSlot(this SkeletonAnimation spine, string slotName)
        {
            if (spine.Skeleton.FindSlot(slotName) == null) return;
            if (!_allClosedSlot.TryGetValue(spine, out List<string> allSlot)) return;
            allSlot.Remove(slotName);
            if (allSlot.Count == 0) _allClosedSlot.Remove(spine);
            else _allClosedSlot[spine] = allSlot;
            spine.Skeleton.SetAttachment(slotName, slotName);
            Log($"slotName: {slotName} 已经打开");
        }

        /// 关闭槽
        public static void CloseSlot(this SkeletonAnimation spine, string slotName)
        {
            if (spine.Skeleton.FindSlot(slotName) == null)
            {
                Debug.Log(string.Format("<color=#ff0000>{0}</color>", $"{slotName} ： 未找到"));
                return;
            }

            if (_allClosedSlot.TryGetValue(spine, out List<string> allSlot))
            {
                if (allSlot.Contains(slotName)) return; // 已经关闭过该槽
                allSlot.Add(slotName);
            }
            else
                allSlot = new List<string>() {slotName};

            _allClosedSlot[spine] = allSlot;
            spine.Skeleton.SetAttachment(slotName, null);
            Log($"slotName: {slotName} 已经关闭");
        }

        /// 打开槽
        public static void OpenSlot(this SkeletonAnimation spine, List<string> allSlotName)
        {
            allSlotName = allSlotName.Distinct().ToList();
            allSlotName.ForEach(spine.OpenSlot);
        }

        /// 关闭槽
        public static void CloseSlot(this SkeletonAnimation spine, List<string> allSlotName)
        {
            allSlotName = allSlotName.Distinct().ToList();
            allSlotName.ForEach(spine.CloseSlot);
        }

        /// 淡入槽
        public static void FadeIn(this SkeletonAnimation spine, List<string> allSlotName, float duration = 2,
            Action cb = null)
        {
            Util.OpenRayMask();
            allSlotName = allSlotName.Distinct().ToList();
            List<Slot> allSlots = new List<Slot>();
            allSlotName.ForEach(delegate(string slotName) { allSlots.Add(spine.Skeleton.FindSlot(slotName)); });
            Util.TweenNumber(0, 1, duration,
                delegate(float f) { allSlots.ForEach(delegate(Slot slot) { slot.A = f; }); }, delegate
                {
                    Util.CloseRayMask();
                    allSlots.ForEach(delegate(Slot slot)
                    {
                        slot.A = 1;
                        Debug.Log($"已淡入槽{slot.Data.AttachmentName}~");
                    });
                    cb?.Invoke();
                });
        }

        /// 淡出槽
        public static void FadeOut(this SkeletonAnimation spine, List<string> allSlotName, float duration = 2,
            Action cb = null)
        {
            Util.OpenRayMask();
            allSlotName = allSlotName.Distinct().ToList();
            List<Slot> allSlots = new List<Slot>();
            allSlotName.ForEach(delegate(string slotName) { allSlots.Add(spine.Skeleton.FindSlot(slotName)); });
            Util.TweenNumber(1, 0, duration,
                delegate(float f) { allSlots.ForEach(delegate(Slot slot) { slot.A = f; }); }, delegate
                {
                    Util.CloseRayMask();
                    cb?.Invoke();
                    allSlots.ForEach(delegate(Slot slot) { slot.A = 0; });
                    spine.CloseSlot(allSlotName);
                });
        }

        /// 渐隐Spine动画
        public static void FadeOut(this SkeletonAnimation spine, float duration = 2, Action cb = null)
        {
            DOTween.To(() => 1, x => spine.Skeleton.A = x, 0f, duration)
                .OnComplete(delegate { cb?.Invoke(); });
        }

        /// 渐入Spine动画
        public static void FadeIn(this SkeletonAnimation spine, float duration = 2, Action cb = null)
        {
            DOTween.To(() => 0f, x => spine.Skeleton.A = x, 1, duration)
                .OnComplete(delegate { cb?.Invoke(); });
        }

        /// <summary>
        /// 更换槽的皮肤
        /// </summary>
        public static void ChangeSkin(this SkeletonAnimation mainSpine, string slotName, Sprite showSp)
        {
            var skeletonSkinData = mainSpine.skeletonDataAsset.GetSkeletonData(true);
            var sourceMaterial = mainSpine.skeletonDataAsset.atlasAssets[0].PrimaryMaterial;
            var key = $"SkinKey_{mainSpine.skeletonDataAsset.name}_{mainSpine.gameObject.name}";
            if (!_skinDic.TryGetValue(key, out var customSkin))
            {
                customSkin = new Skin(key);
                _skinDic.Add(key, customSkin);
            }

            var baseSkin = skeletonSkinData.FindSkin("default");
            var slotData = mainSpine.Skeleton.Data.FindSlot(slotName);
            int changeSlotIdx = slotData.Index;
            Attachment baseAttachment = baseSkin.GetAttachment(changeSlotIdx, slotName);
            Attachment newAttachment = baseAttachment.GetRemappedClone(showSp, sourceMaterial, slotData: slotData,
                spineObj: mainSpine.gameObject);
            customSkin.SetAttachment(changeSlotIdx, slotName, newAttachment);
            mainSpine.Skeleton.SetSkin(customSkin);
            // 使修改生效
            mainSpine.Skeleton.SetSlotsToSetupPose();
            mainSpine.AnimationState.Apply(mainSpine.Skeleton);
            Debug.Log($"已经把槽点：{slotName} 的皮肤更改为：{showSp.name}");
            if (_allClosedSlot.ContainsKey(mainSpine))
            {
                List<string> allSlot = _allClosedSlot[mainSpine];
                _allClosedSlot.Remove(mainSpine);
                mainSpine.CloseSlot(allSlot);
            }
        }

        public static void ChangeSkin(this SkeletonAnimation mainSpine, string slotName, Texture2D texture)
        {
            Sprite sprite = Sprite.Create(texture, new Rect(0, 0, texture.width, texture.height),
                new Vector2(0.5f, 0.5f), 100, 0, SpriteMeshType.FullRect);
            mainSpine.ChangeSkin(slotName, sprite);
        }

        /// 重置为初始皮肤
        public static void ResizeSkin(this SkeletonAnimation mainSpine)
        {
            var skeletonSkinData = mainSpine.skeletonDataAsset.GetSkeletonData(true);
            var baseSkin = skeletonSkinData.FindSkin("default");
            mainSpine.Skeleton.SetSkin(baseSkin);
            // 使修改生效
            mainSpine.Skeleton.SetSlotsToSetupPose();
            mainSpine.AnimationState.Apply(mainSpine.Skeleton);
        }

        /// 清空缓存的皮肤
        public static void ClearCacheSkin(this SkeletonAnimation mainSpine)
        {
            string key = $"SkinKey_{mainSpine.skeletonDataAsset.name}_{mainSpine.gameObject.name}";
            _skinDic.Remove(key);
        }

        public static void ClearAllPmaCache()
        {
            var allCache = AtlasUtilities.pmaCacheDic.Values.ToList();
            allCache.ForEach(delegate(Dictionary<SlotData, Texture2D> dictionary)
            {
                dictionary.Values.ToList().ForEach(Object.Destroy);
            });
            AtlasUtilities.pmaCacheDic.Clear();
        }

        private static void Log(string msg)
        {
            Debug.Log(msg);
        }
    }
}